package service

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/signal"
	"sync"
	"time"

	"github.com/panjf2000/ants/v2"
	"github.com/panjf2000/gnet/v2"
	"github.com/tidwall/btree"
)

type ServiceImp struct {
	l sync.RWMutex
	gnet.BuiltinEventEngine
	Port             int                       //listen port
	LoginTimeout     time.Duration             // login timeout
	PingPongInterval time.Duration             // ping pong interval
	authProvider     AuthProvider              // auth provider
	HandlerMap       map[MessageType][]Handler //处理器
	handlerTimeout   time.Duration             //处理器超时时间
	connTree         *btree.Map[string, *Conn] //已经登录的conn
	worker           *ants.Pool                //	worker pool
	ctxPool          sync.Pool
	close            chan struct{}
}

// OnBoot implements gnet.EventHandler
func (*ServiceImp) OnBoot(eng gnet.Engine) (action gnet.Action) {
	log.Println("service is started")

	return gnet.None
}

// OnClose implements gnet.EventHandler
func (s *ServiceImp) OnClose(c gnet.Conn, err error) (action gnet.Action) {
	log.Println("connect is closed")
	s.removeConn(c)
	return gnet.None
}

// OnOpen implements gnet.EventHandler
func (s *ServiceImp) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	//ACK connect
	log.Printf("OnOpen %p", c)
	return []byte("please login"), gnet.None
}

// OnShutdown implements gnet.EventHandler
func (*ServiceImp) OnShutdown(eng gnet.Engine) {
	log.Println("OnShutdown")
}

// OnTick implements gnet.EventHandler
func (s *ServiceImp) OnTick() (delay time.Duration, action gnet.Action) {
	log.Println("OnTick")
	return time.Second, gnet.None
}

// OnTraffic implements gnet.EventHandler
func (s *ServiceImp) OnTraffic(c gnet.Conn) (action gnet.Action) {
	for c.InboundBuffered() > 0 {
		msg, err := Decode(c)
		if err != nil {
			log.Println("decode error:", err)
			return gnet.Close
		}

		s.worker.Submit(func() {
			conn := s.getConn(c)
			if conn != nil {
				ctx := s.getCtx()
				ctx.IsAuthValid = conn.IsAuthValid
				ctx.Conn = conn
				ctx.Payload = &msg.buffer
				ctx.MsgType = msg.Type
				go s.handleMessage(ctx)
			} else {
				log.Println("conn is nil")
				c.Close(nil)
			}
		})
	}
	return gnet.None
}
func New(opt *Options, opts ...OptFunc) Engine {
	p, err := ants.NewPool(opt.WorkerPoolSize)
	if err != nil {
		panic(err)
	}

	engine := &ServiceImp{
		close:            make(chan struct{}),
		Port:             int(opt.Port),
		LoginTimeout:     opt.LoginTimeout * time.Second,
		PingPongInterval: opt.PingPongInterval * time.Second,
		handlerTimeout:   opt.HandlerTimeout * time.Second,
		worker:           p,
		connTree:         &btree.Map[string, *Conn]{},
		ctxPool: sync.Pool{
			New: newCtx,
		},
		HandlerMap: make(map[MessageType][]Handler),
	}
	for _, v := range opts {
		v(engine)
	}

	engine.AddHandler(Login, func(c *Context) error {
		var req struct {
			Name string `msgpack:"name"`
			Pwd  string `msgpack:"pwd"`
		}
		if err := c.Bind(&req); err != nil {
			return err
		}
		if err := engine.authProvider(req.Name, req.Pwd); err != nil {
			return err
		}
		c.Conn.IsAuthValid = true
		c.Conn.LatestBeatTime = time.Now()
		return nil
	})

	engine.AddHandler(Ping, func(c *Context) error {
		c.LatestBeatTime = time.Now()
		pong := NewMessage().SetType(Pong).SetPayload([]byte("pong"))
		data, err := pong.Bytes()
		if err != nil {
			return err
		}
		_, err = c.Write(data)
		if err != nil {
			return err
		}
		return nil
	})
	return engine
}

// Run implements Service
func (f *ServiceImp) Run() error {
	go func() {
		ch := make(chan os.Signal)
		signal.Notify(ch, os.Interrupt)
		<-ch
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
		defer cancel()
		f.Shutdown(ctx)
	}()
	go f.runCheckPingPong()
	return gnet.Run(f, fmt.Sprintf("tcp://0.0.0.0:%d", f.Port), gnet.WithMulticore(true))
}

func (f *ServiceImp) Shutdown(ctx context.Context) error {
	close(f.close)
	return gnet.Stop(ctx, fmt.Sprintf("tcp://0.0.0.0:%d", f.Port))
}
